
/**
 * @brief Multimedia Card Interface
 * @note PrimeCell PL180
 * @note ARM DDI0172A
 * @note MMC 0-20MHz
 * @note SDC 0-25MHz
 */
typedef struct PL180
{
    /**
     * @brief Power control register
     * @note 1:0 Ctrl 00 poweroff 01 reserved 10 powerup 11 power on
     * @note 5:2 Voltage output voltage
     * @note 6   Open Drain
     * @note 7   Rod Control
     */
    volatile unsigned MCIPower;
    /**
     * @brief Clock control register
     * @note 7:0 clkdiv mclk/2/(clkdiv+1)
     * @note 8   enable
     * @note 9   pwrsave 0 always enabled, 1 clock enabled when bus is active
     * @note 10  bypass 0 disable bypass, 1 enable bypass
     * @note 11  widebus 0 standard bus mode, 1 wide bus mode
     */
    volatile unsigned MCIClock;
    /**
     * @brief Command Argument register
     */
    volatile unsigned MCIArgument;
    /**
     * @brief Command register
     * @note  5:0 cmd index
     * @note  6 response
     * @note  7 long response
     * @note  8 interrupt
     * @note  9 pending
     * @note  10 enable
     */
    volatile unsigned MCICommand;
    /**
     * @brief Response command register.
     * @note Contains the command index field of the last command response received.
     * @note 5:0 response command index
     */
    volatile unsigned MCIRespCmd;
    /**
     * @brief Response register
     * @note Card status
     * @note MCIResponse3~MCIResponse1 are only used when long response
     */
    volatile unsigned MCIResponse0;
    volatile unsigned MCIResponse1;
    volatile unsigned MCIResponse2;
    volatile unsigned MCIResponse3;

    /**
     * @brief Data timer
     * @note data timeout period
     */
    volatile unsigned MCIDataTimer;
    /**
     * @brief Data length register
     * @note 15:0 number of data bytes to be tansferred
     */
    volatile unsigned MCIDataLength;
    /**
     * @brief Data control register
     * @note 0 Enable, data transfer enabled
     * @note 1 Direction, 0 controller to card, 1 card to controller
     * @note 2 Mode, 0 block data transfer, 1 stream data transfer
     * @note 3 DMAEnable
     * @note 7:4 BlockSize, Data block length, 2^N bytes
     */
    volatile unsigned MCIDataCtrl;
    /**
     * @brief Data counter
     * @note Counting down for data length register, Read Only
     * @note 15:0 value
     */
    volatile unsigned MCIDataCnt;
    /**
     * @brief Status register
     * @note 0 CmdCrcFail
     * @note 1 DataCrcFail
     * @note 2 CmdTimeOut
     * @note 3 DataTimeOut
     * @note 4 TxUnderrun
     * @note 5 RxOverrun
     * @note 6 CmdRespEnd
     * @note 7 CmdSent
     * @note 8 DataEnd
     * @note 9 StartBitErr
     * @note 10 DataBlockEnd
     * @note 11 CmdActive
     * @note 12 TxActive
     * @note 13 RxActive
     * @note 14 TxFifoHalfEmpty
     * @note 15 RxFifoHalfFull
     * @note 16 TxFifoFull
     * @note 17 RxFifoFull
     * @note 18 TxFifoEmpty
     * @note 19 RxFifoEmpty
     * @note 20 TxDataAvailable
     * @note 21 RxDataAvailable
     */
    volatile unsigned MCIStatus;
    /**
     * @brief Clear register
     * @note 0  Write Clears CmdCrcFail flag
     * @note 1  Write Clears DataCrcFail flag
     * @note 2  Write Clears CmdTimeOut flag
     * @note 3  Write Clears DataTimeOut flag
     * @note 4  Write Clears TxUnderrun flag
     * @note 5  Write Clears RxOverrun flag
     * @note 6  Write Clears CmdRespEnd flag
     * @note 7  Write Clears CmdSent flag
     * @note 8  Write Clears DataEnd flag
     * @note 9  Write Clears StartBitErr flag
     * @note 10 Write Clears DataBlockEnd flag
     */
    volatile unsigned MCIClear;
    /**
     * @brief Interrupt 0 mask register
     * @note 0 Mask0 Read/write Mask CmdCrcFail flag
     * @note 1 Mask1 Read/write Mask DataCrcFail flag
     * @note 2 Mask2 Read/write Mask CmdTimeOut flag
     * @note 3 Mask3 Read/write Mask DataTimeOut flag
     * @note 4 Mask4 Read/write Mask TxUnderrun flag
     * @note 5 Mask5 Read/write Mask RxOverrun flag
     * @note 6 Mask6 Read/write Mask CmdRespEnd flag
     * @note 7 Mask7 Read/write Mask CmdSent flag
     * @note 8 Mask8 Read/write Mask DataEnd flag
     * @note 9 Mask9 Read/write Mask StartBitErr flag
     * @note 10 Mask10 Read/write Mask DataBlockEnd flag
     * @note 11 Mask11 Read/write Mask CmdActive flag
     * @note 12 Mask12 Read/write Mask TxActive flag
     * @note 13 Mask13 Read/write Mask RxActive flag
     * @note 14 Mask14 Read/write Mask TxFifoHalfEmpty flag
     * @note 15 Mask15 Read/write Mask RxFifoHalfFull flag
     * @note 16 Mask16 Read/write Mask TxFifoFull flag
     * @note 17 Mask17 Read/write Mask RxFifoFull flag
     * @note 18 Mask18 Read/write Mask TxFifoEmpty flag
     * @note 19 Mask19 Read/write Mask RxFifoEmpty flag
     * @note 20 Mask20 Read/write Mask TxDataAvlbl flag
     * @note 21 Mask21 Read/write Mask RxDataAvlbl flag
     */
    volatile unsigned MCIMask0;
    /**
     * @brief Interrupt 1 mask register
     * @note refer to MCIMask1
     */
    volatile unsigned MCIMask1;
    /**
     * @brief SD Card select register
     * @note 3:0 SDCard, secure digital memory card address
     */
    volatile unsigned MCISelect;
    /**
     * @brief Fifo counter
     * @note 14:0 remaining data
     */
    volatile unsigned MCIFifoCnt;
    unsigned reserve[13];
    /**
     * @brief Data Fifo register
     * @note Fifo Data
     */
    volatile unsigned MCIFifo;
} *PL180;


#define MMC_CMD_GO_IDLE_STATE		0
#define MMC_CMD_SEND_OP_COND		1
#define MMC_CMD_ALL_SEND_CID		2
#define MMC_CMD_SET_RELATIVE_ADDR	3
#define MMC_CMD_SET_DSR			4
#define MMC_CMD_SWITCH			6
#define MMC_CMD_SELECT_CARD		7
#define MMC_CMD_SEND_EXT_CSD		8
#define MMC_CMD_SEND_CSD		9
#define MMC_CMD_SEND_CID		10
#define MMC_CMD_STOP_TRANSMISSION	12
#define MMC_CMD_SEND_STATUS		13
#define MMC_CMD_SET_BLOCKLEN		16
#define MMC_CMD_READ_SINGLE_BLOCK	17
#define MMC_CMD_READ_MULTIPLE_BLOCK	18
#define MMC_CMD_WRITE_SINGLE_BLOCK	24
#define MMC_CMD_WRITE_MULTIPLE_BLOCK	25
#define MMC_CMD_ERASE_GROUP_START	35
#define MMC_CMD_ERASE_GROUP_END		36
#define MMC_CMD_ERASE			38
#define MMC_CMD_APP_CMD			55
#define MMC_CMD_SPI_READ_OCR		58
#define MMC_CMD_SPI_CRC_ON_OFF		59

#define SD_CMD_SEND_RELATIVE_ADDR	3
#define SD_CMD_SWITCH_FUNC		6
#define SD_CMD_SEND_IF_COND		8

#define SD_CMD_APP_SET_BUS_WIDTH	6
#define SD_CMD_ERASE_WR_BLK_START	32
#define SD_CMD_ERASE_WR_BLK_END		33
#define SD_CMD_APP_SEND_OP_COND		41
#define SD_CMD_APP_SEND_SCR		51

#define CONFIG_ARM_PL180_MMCI_CLOCK_FREQ 6250000
#define MMC_RSP_PRESENT (1 << 0)
#define MMC_RSP_136	(1 << 1)		/* 136 bit response */
#define MMC_RSP_CRC	(1 << 2)		/* expect valid crc */
#define MMC_RSP_BUSY	(1 << 3)		/* card may send busy */
#define MMC_RSP_OPCODE	(1 << 4)		/* response contains opcode */

#define MMC_RSP_NONE	(0)
#define MMC_RSP_R1	(MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)
#define MMC_RSP_R1b	(MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE| \
			MMC_RSP_BUSY)
#define MMC_RSP_R2	(MMC_RSP_PRESENT|MMC_RSP_136|MMC_RSP_CRC)
#define MMC_RSP_R3	(MMC_RSP_PRESENT)
#define MMC_RSP_R4	(MMC_RSP_PRESENT)
#define MMC_RSP_R5	(MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)
#define MMC_RSP_R6	(MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)
#define MMC_RSP_R7	(MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)


int send_command(PL180 model, int cmd, int arg, int resp)
{
    model->MCIArgument = arg;
    model->MCICommand = 0b10000000000 | (resp<<6) | cmd;

    return 0;
}

void init_sdc(PL180 model)
{
    model->MCIPower = 0xBF;
    model->MCIClock = 0xC6;
    unsigned RCA = 0x45670000;               // QEMU's hard-coded RCA
    send_command(model, 0, 0, MMC_RSP_NONE); // idle
    send_command(model, 55, 0, MMC_RSP_R1);  // ready

    send_command(model, 41, 0x12345F00, MMC_RSP_R3); // arg
    send_command(model, 2, 0, MMC_RSP_R2);  // ask card cid

    send_command(model, 3, RCA, MMC_RSP_R1);  // assign rca
    send_command(model, 7, RCA, MMC_RSP_R1);  // transfer state
    send_command(model, 16, 512, MMC_RSP_R1); // set data block
    model->MCIMask0 = (1<<21) | (1<<18);
}


#include "typedef.h"
#include "astralapi.h"


static int pl180_read(AstralDevice dev, char *buff, int buff_size, int *bytes_read)
{
    PL180 pm = CAST_AS(PL180, dev->vbase);

    int count = CAST_AS(int, bytes_read);
    int sector = buff_size;

    int cmd = 17;

    for (int s = sector; s < sector + count; s++, buff += 512)
    {
        pm->MCIDataTimer = 0xffff0000;
        pm->MCIDataLength = 512;
        pm->MCIDataCtrl = 0x93;

        int arg = (s * 512);
        send_command(pm, cmd, arg, MMC_RSP_R1);

        while (!(pm->MCIStatus & (1 << 21)))
        {
        }

        int bytes = 512;
        unsigned *read = CAST_AS(unsigned *, buff);
        while (bytes)
        {
            *read = pm->MCIFifo;
            read++;
            bytes -= sizeof(unsigned);
            pm->MCIStatus; //must read out status
        }
    }
    return 0;
}

static int pl180_write(AstralDevice dev, const char *buff, int buff_size, int *bytes_write)
{
    kerror("sdc write\n");
    for (int i = 0; i < buff_size; i++)
    {
        char c = *(buff + i);
        if(!c)
        {
            break;
        }
    }
    return 0;
}

static int pl180_open(AstralDevice dev, object arg)
{
    AstralDevice d = CAST_AS(AstralDevice, dev);
    void *base = d->vbase;
    init_sdc(CAST_AS(PL180, base));
    return 0;
}

static int pl180_close(AstralDevice dev, object arg)
{
    return 0;
}




static struct AstralDriver pl180_driver = {
    .dwrite = pl180_write,
    .dread = pl180_read,
    .dopen = pl180_open,
    .dclose = pl180_close,
    .description = "pl180 sdcard driver"};

static void pl180_register(object arg)
{
    boolean status = register_driver(MIDv4(96, 1, 0, 3), &pl180_driver);
    kprint("register for %s done, status %d\n",
           pl180_driver.description,
           status);
}

STATIC_REGISTER_DRIVER(pl180_register)
